﻿using System;
using System.Xml;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Globalization;

namespace BasecampSync
{
    class BasecampApi
    {
        private readonly string myUsername;
        private readonly string myPassword;

        public BasecampApi(string username, string password)
        {
            this.myUsername = username;
            this.myPassword = password;
        }

        public List<Milestone> GetMilestones(UInt32 projectId)
        {
            XmlDocument reply = SendRequest("/projects/" + projectId + "/milestones/list", "");
            XmlNodeList milestones = reply.GetElementsByTagName("milestone");

            List<Milestone> collectedMilestones = new List<Milestone>();

            // Extract milestones from XML reply
            foreach (XmlElement milestone in milestones)
            {
                Milestone toInsert = new Milestone();

                // Id, project id
                try
                {
                    toInsert.Id = Convert.ToUInt32(milestone.GetElementsByTagName("id")[0].InnerText);
                    toInsert.ProjectId = Convert.ToUInt32(milestone.GetElementsByTagName("project-id")[0].InnerText);
                }
                catch
                {
                    // Malformed or too large integer
                    continue;
                }

                // Complete status
                toInsert.Completed =
                    (milestone.GetElementsByTagName("completed")[0].InnerText == "true");

                toInsert.Title = milestone.GetElementsByTagName("title")[0].InnerText;

                string createdInput = milestone.GetElementsByTagName("created-on")[0].InnerText;
                toInsert.Created = DateTime.ParseExact(createdInput, "yyyy-MM-ddTHH:mm:ssZ", DateTimeFormatInfo.InvariantInfo);

                string deadlineInput = milestone.GetElementsByTagName("deadline")[0].InnerText;
                toInsert.Deadline = DateTime.ParseExact(deadlineInput, "yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);

                collectedMilestones.Add(toInsert);
            }

            return collectedMilestones;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="personId"></param>
        /// <returns></returns>
        public Person GetPersonalData(UInt32 personId)
        {
            XmlDocument reply = SendRequest("/contacts/person/" + personId.ToString(), "");

            Person cur = new Person();

            try
            {
                cur.Id = Convert.ToUInt32(reply.GetElementsByTagName("id")[0].InnerText);
            }
            catch
            {
                return new Person();
            }

            cur.Email = reply.GetElementsByTagName("email-address")[0].InnerText;
            cur.FirstName = reply.GetElementsByTagName("first-name")[0].InnerText;
            cur.LastName = reply.GetElementsByTagName("last-name")[0].InnerText;

            return cur;
        }

        public XmlDocument InsertTodoItem(UInt32 listId, string content)
        {
            string request = "<request><content>" + content + "</content></request>";
            XmlDocument reply = SendRequest("/todos/create_item/" + listId.ToString(), request);

            return reply;
        }

        public void MarkTodoAsComplete(UInt32 itemId)
        {
            SendRequest("/todos/complete_item/" + itemId.ToString(), "");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="todoListId"></param>
        /// <returns></returns>
        public List<TodoItem> GetTodoItems(UInt32 todoListId)
        {
            XmlDocument reply = SendRequest("/todos/list/" + todoListId.ToString(), "");
            XmlNodeList items = reply.GetElementsByTagName("todo-item");

            List<TodoItem> collectedItems = new List<TodoItem>();

            foreach (XmlElement item in items)
            {
                //UInt32 id = 0;
                UInt32 creatorId = 0;

                TodoItem toInsert = new TodoItem();

                try
                {
                    toInsert.Id = Convert.ToUInt32(item.GetElementsByTagName("id")[0].InnerText);

                    // Creator stuff
                    creatorId = Convert.ToUInt32(item.GetElementsByTagName("creator-id")[0].InnerText);
                    toInsert.Creator = GetPersonalData(creatorId);
                }
                catch
                {
                    continue;
                }

                toInsert.Completed =
                    (item.GetElementsByTagName("completed")[0].InnerText == "true");

                toInsert.Content = item.GetElementsByTagName("content")[0].InnerText;

                // Date stuff
                // Raw format: 2007-06-10T14:45:19Z
                string createdInput = item.GetElementsByTagName("created-on")[0].InnerText;
                DateTime created = DateTime.ParseExact(createdInput, "yyyy-MM-ddTHH:mm:ssZ", DateTimeFormatInfo.InvariantInfo);

                collectedItems.Add(toInsert);
            }

            return collectedItems;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<TodoList> GetTodoLists(UInt32 projectId)
        {
            XmlDocument reply = SendRequest("projects/" + projectId.ToString() + "/todos/lists", "");
            XmlNodeList lists = reply.GetElementsByTagName("todo-list");

            List<TodoList> collectedLists = new List<TodoList>();

            foreach (XmlElement list in lists)
            {
                UInt32 id = 0;

                try
                {
                    id = Convert.ToUInt32(list.GetElementsByTagName("id")[0].InnerText);
                }
                catch
                {
                    // Malformed or too large integer
                    continue;
                }

                string name = list.GetElementsByTagName("name")[0].InnerText;

                if (id != 0 && !String.IsNullOrEmpty(name))
                {
                    // Valid todo list
                    TodoList toInsert = new TodoList();
                    toInsert.Id = id;
                    toInsert.Name = name;

                    // Get items in the current list
                    toInsert.TodoItems = GetTodoItems(id);

                    collectedLists.Add(toInsert);
                }
            }

            return collectedLists;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<Project> GetProjectList()
        {
            List<Project> collectedProjects = new List<Project>();

            XmlDocument reply = SendRequest("project/list", "");
            XmlNodeList projects = reply.GetElementsByTagName("project");

            foreach (XmlNode project in projects)
            {
                if (!project.HasChildNodes)
                {
                    continue;
                }

                string projectName = "";
                UInt32 projectId = 0;

                foreach (XmlNode node in project.ChildNodes)
                {
                    // We need the project id and name
                    if (node.Name == "name")
                    {
                        projectName = node.InnerText;
                    }

                    if (node.Name == "id")
                    {
                        projectId = Convert.ToUInt32(node.InnerText);
                    }
                }

                // Add cur project to list
                if (projectName != "" && projectId != 0)
                {
                    // Prepare
                    Project newProject = new Project();
                    newProject.Id = projectId;
                    newProject.Name = projectName;
                    newProject.Milestones = GetMilestones(projectId);

                    // Add to collection
                    collectedProjects.Add(newProject);

                    // Debug output
                    System.Diagnostics.Debug.WriteLine("Adding Project:");
                    System.Diagnostics.Debug.WriteLine("\tid: " + newProject.Id + " name: " + newProject.Name);
                }
            }

            return collectedProjects;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public XmlDocument SendRequest(string command, string request)
        {
            string url = Config.BasecampUrl + command;

            // Setting up request
            WebRequest webRequest = WebRequest.Create(url);

            webRequest.Method = "post";

            // Authentication
            string auth = Convert.ToBase64String(
                Encoding.ASCII.GetBytes(string.Concat(this.myUsername, ":", this.myPassword)));
            webRequest.Headers.Add("Authorization", "Basic " + auth);


            webRequest.ContentType = "application/xml";

            ((HttpWebRequest)webRequest).AllowWriteStreamBuffering = true;

            //webRequest.Headers.Add("Content-Length", request.Length.ToString());
            //webRequest.ContentLength = request.Length;

            // Debug out
            using (StreamWriter sw = new StreamWriter(webRequest.GetRequestStream()))
            {
                sw.WriteLine(request);
            }

            // Setting up response
            WebResponse response = webRequest.GetResponse();

            using (StreamReader sr = new StreamReader(response.GetResponseStream()))
            {
                XmlDocument ret = new XmlDocument();
                ret.Load(sr.BaseStream);
                return ret;
            }
        }
    }
}
